// ITCServer.java

package com.ibm.tspaces.contrib.ITC;

/*
**  Licensed Materials - Property of IBM
**
**  (C) COPYRIGHT IBM Corp. 1996, 1997, 1998  All rights reserved
**
**  US Government Users Restricted Rights - Use, duplication or
**  disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
*/


import java.util.*;
import java.awt.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import com.ibm.tspaces.*;

/*
************************************************************************
** Class ITCServer **
*********************
*/
/**
** ITCServer is the Internet T Space Chat server
** ITCServer sits on the T Space watching for control events, and parsing
** them as appropriate.  Also, it takes channel events and relays them.
**
** @see TupleSpace, ITCClient
** @version $Revision: 2.1 $ $Date: 1999/11/05 22:22:37 $
** @author <a href="mailto:kevin@cs.hmc.edu"> Kevin Eustice </a>
**
************************************************************************
*/
public class ITCServer extends Frame
{
  //
  // The name of the TupleSpace to be used for the clipboard
  //
  static String    _spaceName  = "ITC";

  //
  // The name of the server that hosts the tuple space
  //
  static String    _serverName = "tspaces";

  static TupleSpace _ts;	// the Tuplespace we'll connect to

  static Vector _users;		// the users connected to the ITC
  static Vector _channels;	// valid channels
  //currently, users and channels don't expire.  I want a Tuple that has an
  //automatic expiration timeout.  Future addition to T Space?

  /*
  ***************************************************************************
  ** ITCServer ** (Constructor)
  ***************
  */
  /**
  ** The constructor initializes the user and channel variables, and
  ** attempts to connect to the T Space
  ** @param
  ** @return
  ** @exception
  ***************************************************************************
  */
  public ITCServer()
  {

    _users = new Vector(0);
    _channels = new Vector(0);
    try {
      _ts = new TupleSpace(_spaceName, _serverName);
    } catch (Exception e)
      {
	System.err.println("Whoops!  Error connecting to the T Space.");
      }; // end catch
  } // end constructor


  /*
  ***************************************************************************
  ** serve **
  ***********
  */
  /**
  ** Starts serving the ITC Space
  **
  ***************************************************************************
  */

  static void serve()
  {
    while(true) {
      try {
	System.out.println(_users +" "+ _channels);
	try {
	  Thread.sleep(300);
	} catch (Exception te)
	  {
	    System.err.println("Thread exception");
	  };
	System.out.println("Getting Tuples...");
	Tuple result =
	 _ts.waitToTake(new Tuple("ITCIN",Field.makeField("java.lang.String"),
			Field.makeField("java.lang.String"),
			Field.makeField("java.lang.String")));
	String who = (String) result.getField(1).value();
	String chancmd = (String) result.getField(2).value();
	String data = (String) result.getField(3).value();
	System.out.println("Data:" + who + " " + chancmd + " " + data);

	if(chancmd.equals("NEWUSER")) {
	  Object newuser = null;
	  try {
	    newuser = findUser(who);
	  } catch (NoSuchElementException nsee) {
	    newuser = new User(who,data);
	    System.out.println("Adding valid newuser.");
	    _users.addElement(newuser);
	    addUserToChannel("#Lobby",who);
	    _ts.write(new Tuple("ITCCMD",who,"CREATE","SUCC"));
	    _ts.write(new Tuple("ITCCMD",who,"CREATE","SUCC"));
	    _ts.write(new Tuple("ITCOUT",who,"CREATE",
	    "Welcome to turing.cs.hmc.edu!\n"+
	    "This is a prototype ITC server run at Harvey Mudd College.\n"+
	    "**********************************************************\n"+
	    "Supported commands:\n"+
	    "/nick <username>    (changes username)\n"+
	    "/join <channel>     (change to or create channel)\n"+
	    "/list               (shows existing channels)\n"+
	    "/msg <username> <message> (whisper a message to someone)\n"+
	    "/who                (see a list of users in channel\n"+
	    "**********************************************************\n"));

	    _ts.write(new Tuple("ITCOUT",who,"#Lobby","Welcome to #Lobby\n"));

	    continue;
	  } // end catch

	  if(((User) newuser).checkPass(data)) {
	    System.out.println("Verified olduser.");
	    addUserToChannel("#Lobby",who);
	    _ts.write(new Tuple("ITCCMD",who,"CREATE","SUCC"));
	    _ts.write(new Tuple("ITCOUT",who,"CREATE",
	    "Welcome to turing.cs.hmc.edu!\n"+
	    "This is a prototype ITC server run at Harvey Mudd College.\n"+
	    "**********************************************************\n"+
	     "Supported commands:\n"+
	    "/nick <username>    (changes username)\n"+
	    "/join <channel>     (change to or create channel)\n"+
	    "/list               (shows existing channels)\n"+
	    "/mesg <username> <message> (whisper a message to someone)\n"+
	    "/who                (see a list of user in channel\n"+
	    "**********************************************************\n"));
	    _ts.write(new Tuple("ITCOUT",who,"#Lobby","Welcome to #Lobby\n"));

	  } else {
	    System.out.println("Bad user.");
	    _ts.write(new Tuple("ITCCMD",who,"CREATE","FAIL"));

	  } //end else
	  continue;
	} // end if


	if(chancmd.equals("LIST")) {
	  Enumeration enum =  _channels.elements();
	  _ts.write(new Tuple("ITCOUT",who,"LIST","Available Channels:\n"));
	  while(enum.hasMoreElements()) {
	    Channel tmp = (Channel) enum.nextElement();
	    _ts.write(new Tuple("ITCOUT",who,"LIST",tmp.getChannel()+"(" + tmp.numUsers() + ")"+"\n"));
	  }
	  continue;
	}

	if(chancmd.equals("WHO")) {
	  Channel chan;
	  try {
	    chan = (Channel) findChannel(data);
	  } catch (NoSuchElementException nsee)
	    {
	      System.out.println("Couldn't find channel");
	      continue;
	    } //end catch

	  Vector tempusers = chan.getUsers();
	  System.out.println("Found channel " + chan.getChannel());
	  _ts.write(new Tuple("ITCOUT",who, data,"Users in channel:\n"));
	  for(int i = 0;i<tempusers.size();i++)
	    _ts.write(new Tuple("ITCOUT",who, data,
		      (java.io.Serializable) tempusers.elementAt(i) + "\n"));
	} // end for

	if(chancmd.equals("NICKCHG")) {
	  System.out.println("Nick change requested.");
	  try {
	    Object newuser = findUser(data);
	    System.out.println("Username: " + ((User) newuser).getName());
	  } catch (NoSuchElementException nsee) {
	    try {
	      System.out.println("New nick not in use.");
	      User user = (User) findUser(who);
	      System.out.println("Found user: " + user.getName());
	      user.chgName(data);
	      System.out.println("Finding channel: " + user.getChannel());
	      Channel tmpChan = (Channel) findChannel(user.getChannel());
	      System.out.println("Found channel: " + tmpChan.getChannel());
	      _channels.removeElement(tmpChan);
	      tmpChan.chgUser(who,data);
	      Vector tempusers = tmpChan.getUsers();

	      for(int i = 0;i<tempusers.size();i++)
	      System.out.println(tempusers.elementAt(i));

	      _channels.addElement(tmpChan);
	      _ts.write(new Tuple("ITCCMD",who,"NICKCHG","SUCC"));
	      _ts.write(new Tuple("ITCOUT",who,"TEST",who +
				  " is now known as " + data +".\n"));
	      System.out.println("Nick change succeeded.");
	      continue;
	    } catch (NoSuchElementException nsee2) {
	      System.err.println("Error in findChannel.");
	    } // end catch
	  } // end catch

	  _ts.write(new Tuple("ITCCMD",who,"NICKCHG","FAIL"));
	  System.out.println("Nick change failed.");
	} // end if

	if(chancmd.equals("JOIN")) {
	  User user = (User) findUser(who);
	  System.out.println("Finding channel: " + user.getChannel());
	  Channel tmpChan = (Channel) findChannel(user.getChannel());
	  System.out.println("Found channel: " + tmpChan.getChannel());
	  Vector tempusers = tmpChan.getUsers();
	  tmpChan.rmUser(who);
	  user.setChannel(data);
	  addUserToChannel(data,who);
	  _ts.write(new Tuple("ITCCMD",who,"JOIN","SUCC"));
	  _ts.write(new Tuple("ITCOUT",who,data,"Welcome to " + data + "\n"));

	  for(int i = 0;i<tempusers.size();i++) {
	    _ts.write(new Tuple("ITCOUT",
		    (java.io.Serializable) tempusers.elementAt(i),
		    chancmd,who + " has left the channel.\n"));
	  } // end for
	  tempusers = ((Channel) findChannel(data)).getUsers();

	  for(int i = 0;i<tempusers.size();i++) {
	      _ts.write(new Tuple("ITCOUT",
		    (java.io.Serializable) tempusers.elementAt(i),
		    chancmd,who + " has joined the channel.\n"));
	  } // end for
	  _ts.write(new Tuple("ITCIN",who,"WHO",data));
	  System.out.println("Join succeeded.");
	  continue;

	} else {
	  System.out.println("We have a channel message on channel " + chancmd);
	  Channel chan = null;
	  try {
	    chan = (Channel) findChannel(chancmd);
	  } catch (NoSuchElementException nsee) {
	    System.out.println("Couldn't find channel");continue;
	  } // end catch

	  Vector tempusers = chan.getUsers();
	  System.out.println("Found channel " + chan.getChannel());
	  for(int i = 0;i<tempusers.size();i++) {
	    _ts.write(new Tuple("ITCOUT",(java.io.Serializable) tempusers.elementAt(i),chancmd,data));
	    System.out.println(tempusers.elementAt(i));
	  } // end for
	} //end else
      } catch (Exception ie)
	{
	  System.err.println("Exception in writing to T Space.");
	}; // end catch
    } // end while

  } //end server



  /*
  ***************************************************************************
  ** findUser **
  *************
  ** WRITTEN:
  ** UPDATED:
  */
  /**
  ** Searches in the _vector for the user with name name_
  **
  ** @param name_ (String) the user to look for
  ** @return Object that should be a User object
  ** @exception  NoSuchElementException if the element is not to be found
  ***************************************************************************
  */
  static Object findUser(String name_) throws NoSuchElementException
  {

     Enumeration enum =  _users.elements();

     while(enum.hasMoreElements()) {
       User tmp = (User) enum.nextElement();
       if(tmp.getName().equals(name_)) return tmp;
     } // end while

     throw new NoSuchElementException();
  } // end findUser




  /*
  ***************************************************************************
  ** findChannel **
  *****************
  ** WRITTEN:
  ** UPDATED:
  */
  /**
  ** Find a given channel in the channel vector
  **
  ** @param name_
  ** @return Object -- should be a channel object
  ** @exception  NoSuchElementException if channel isn't valid
  ***************************************************************************
  */
  static Object findChannel(String name_) throws NoSuchElementException
  {
    Enumeration enum =  _channels.elements();

    while(enum.hasMoreElements()) {
      Channel tmp = (Channel) enum.nextElement();
      if(tmp.getChannel().equals(name_))
	return tmp;
    } // end while

    throw new NoSuchElementException();

  } // end findChannel

  /*
  ***************************************************************************
  ** addUserToChannel **
  **********************
  */
  /**
  ** Add a user to a channel.  If the channel doesn't exist, create it.
  **
  ** @param channel_  Channel name
  ** @param user_  User name
  ***************************************************************************
  */
  static void addUserToChannel(String channel_, String user_)
  {
    Channel chan;
    try {
      chan = (Channel) findChannel(channel_);
    } catch (NoSuchElementException nsee) {

      chan = new Channel(channel_, user_);
      System.out.println("Creating new channel: " + chan.getChannel());
      _channels.addElement(chan);
      return;
    } // end catch

    _channels.removeElement(chan);
    chan.addUser(user_);
    _channels.addElement(chan);

    return;
  } // end addUserToChannel


  /*
  ***************************************************************************
  ** Main **
  **********
  */
  /**
  ** Main's usually go at the end of the class so that we can find them
  ** consistently.
  **
  ** @param String[] args -- args[0] should be the servername.
  ***************************************************************************
  */

  public static void main (String args[])
  {

    try {
      if( args.length != 0 ){
        _serverName = args[0]; //get our servername from command line
      }
    } catch (Exception e) {
      System.out.println(e.getMessage());
      e.printStackTrace();
    }  //end catch
    //create new ITCServer
    ITCServer itcserver = new ITCServer();
    //start the server
    itcserver.serve();

  } // end Main

}





/*
************************************************************************
** Class User **
****************
*/
/**
** This describes a User.  Currently it only manages a single channel.
** This will eventually become a vector of channels that you're subscribed
** to.
** Graphic info will go here as well, describing you to the world
** in a comic strip form?  Sure....why not.
**
** @see TupleSpace, ITCClient
** @version $Revision: 2.1 $ $Date: 1999/11/05 22:22:37 $
** @author <a href="mailto:kevin@cs.hmc.edu"> Kevin Eustice </a>
**
************************************************************************
*/



class User {

  String _username;
  String _channel;
  String _password;

  /*
  ***************************************************************************
  ** User ** (Constructor)
  **********
  */
  /**
  ** The constructor initializes the user and channel variables
  **
  ** @param name_ the user name
  ** @param pass_ the user's apssword
  ** @return
  ** @exception
  ***************************************************************************
  */

  public User(String name_, String pass_)
  {
    _username = name_;
    _password = pass_;
    _channel = "#Lobby";
  }

  /*
  ***************************************************************************
  ** checkPass **
  ***************
  */
  /**
  ** This checks the user's password against an input string and returns a
  ** a boolean to indicate if the password was valid.
  **
  ** @param pass_ the input password
  ** @return boolean Was the password valid?
  ** @exception none
  ***************************************************************************
  */
  boolean checkPass(String pass_)
  {
    if(_password.equals(pass_)) return true;
    return false;
  }
  /*
  ***************************************************************************
  ** getName **
  *************
  */
  /**
  ** This returns the username
  **
  ** @return String : the user's name
  ** @exception none
  ***************************************************************************
  */
  String getName()
  {
    return _username;
  }
  /*
  ***************************************************************************
  ** chgName **
  *************
  */
  /**
  ** This changes the username, and returns the new username
  ** @param name_ the new name
  ** @return String : the user's name
  ** @exception none
  ***************************************************************************
  */
  String chgName(String name_)
  {
    _username = name_;
    return name_;
  }

  /*
  ***************************************************************************
  ** getChannel **
  ****************
  */
  /**
  ** This returns the current channel the user is in
  ** @return String - the user's active channel
  ** @exception none
  ***************************************************************************
  */
  String getChannel()
  {
    return _channel;
  }
  /*
  ***************************************************************************
  ** setChannel **
  ****************
  */
  /**
  ** This set's the user's channel
  ** @param chan_  the user's new active channel
  ** @exception none
  ***************************************************************************
  */

  void setChannel(String chan_)
  {
    _channel = chan_;
  }

}

/*
************************************************************************
** Class Channel **
*******************
*/
/**
** This describes a channel, and keeps track of who's subscribed to it.
**
** @see TupleSpace, ITCClient
** @version $Revision: 2.1 $ $Date: 1999/11/05 22:22:37 $
** @author <a href="mailto:kevin@cs.hmc.edu"> Kevin Eustice </a>
**
************************************************************************
*/

class Channel {

  //class variables

    Vector _users;
    String _channel;


  /*
  ***************************************************************************
  ** Channel ** (Constructor)
  *************
  */
  /**
  ** The constructor initializes the channel variables
  **
  ** @param chan_ the channel name
  ** @param user_ A user to place in the channel initially
  ** @return
  ** @exception
  ***************************************************************************
  */

  public Channel(String chan_, String user_)
  {
    _users = new Vector(0);
    _channel = chan_;
    addUser(user_);
  }

  /*
  ***************************************************************************
  ** getChannel **
  ****************
  */
  /**
  ** This returns the channel name
  ** @return String - the channel name
  ** @exception none
  ***************************************************************************
  */

  String getChannel()
  {
    return _channel;
  }

  /*
  ***************************************************************************
  ** numUsers **
  **************
  */
  /**
  ** This returns the number of users in the channel
  ** @return int Number of users
  ** @exception none
  ***************************************************************************
  */

  int numUsers ()
  {
    return _users.size();
  }

  /*
  ***************************************************************************
  ** addUser **
  *************
  */
  /**
  ** This adds a user to the channel
  ** @param username_ The user to add
  ** @exception none
  ***************************************************************************
  */

  void addUser(String username_)
  {

    _users.removeElement(username_); //don't allow duplication

    _users.addElement(username_);

  }
  /*
  ***************************************************************************
  ** chgUser **
  *************
  */
  /**
  ** This replaces one user with another
  ** Usually used for NICKCHG ops.
  ** @param username_ The user to remove
  ** @param newUsername_ The user to add
  ** @exception none
  ***************************************************************************
  */

  void chgUser(String username_, String newUsername_)
  {
    _users.removeElement(username_);
    _users.addElement(newUsername_);
  }

  /*
  ***************************************************************************
  ** rmUser **
  *************
  */
  /**
  ** This removes one user
  ** @param username_ The user to remove
  ** @exception none
  ***************************************************************************
  */

  void rmUser(String user_)
  {
    _users.removeElement(user_);
  }

  /*
  ***************************************************************************
  ** getUsers **
  **************
  */
  /**
  ** Returns a vector of users in the channel
  ** @return Vector the users in the channel
  ** @exception none
  ***************************************************************************
  */

  Vector getUsers()
  {
    return (Vector) _users.clone();
  }


}

/*
$History: ITCServer.java $
 * 
 * *****************  Version 2  *****************
 * User: Toby         Date: 3/16/98    Time: 1:36a
 * Updated in $/GCS/Development/TSpaces/Java/com/ibm/tspaces/contrib/ITC
 * Latest fixed up version from Kevin
*/

/* $Log: ITCServer.java,v $
/* Revision 2.1  1999/11/05 22:22:37  estesp
/* Update revision number to 2.1 on all files
/*
/* Revision 1.1.1.1  1999/11/05 16:24:52  estesp
/* Imported 2.1.0 release into Austin CVS
/*
 * Revision 1.2  1999/06/17 05:39:37  thodes
 *   Updated the inital checkin to append the "Log" field and
 * convert CR/LF to CR (for win/unix interop).
 *
 *   The inital checkin lower-cased directrory names, and omitted
 * many dirs/files that will added later, e.g., MoDAL stuff.
 *
 * All of the above will need to be done to all VSS-to-CVS
 * converted sources.  In summary, in moving from VSS to CVS, do this:
 *  - lower-case dir names
 *  - CR/LF --> CR
 *  - append CVS "Log" entties
 *
 * (a script that helps with #2 and #3 is in
 *     tspaces1:~thodes/bin/update-keywords.sh)
 * 
 */


